package com.javabi.common.concurrent;

import static com.google.common.base.Objects.equal;

import java.util.concurrent.Callable;

public class CallableRunnable<R> implements Callable<R> {

	private final Runnable runnable;
	private final R returnValue;
	private final Exception exception;

	public CallableRunnable(Runnable runnable, R returnValue) {
		if (runnable == null) {
			throw new NullPointerException("runnable");
		}
		this.runnable = runnable;
		this.returnValue = returnValue;
		this.exception = null;
	}

	public CallableRunnable(Runnable runnable, Exception exception) {
		if (runnable == null) {
			throw new NullPointerException("runnable");
		}
		if (exception == null) {
			throw new NullPointerException("exception");
		}
		this.runnable = runnable;
		this.returnValue = null;
		this.exception = exception;
	}

	public CallableRunnable(Runnable runnable) {
		if (runnable == null) {
			throw new NullPointerException("runnable");
		}
		this.runnable = runnable;
		this.returnValue = null;
		this.exception = null;
	}

	@Override
	public R call() throws Exception {
		runnable.run();
		return returnValue;
	}

	@Override
	public int hashCode() {
		int hash = runnable.hashCode();
		if (returnValue != null) {
			hash *= returnValue.hashCode();
		}
		if (exception != null) {
			hash *= exception.hashCode();
		}
		return hash * 1999;
	}

	@Override
	public boolean equals(Object object) {
		if (object == this) {
			return true;
		}
		if (object instanceof CallableRunnable) {
			CallableRunnable<?> callable = (CallableRunnable<?>) object;
			if (!equal(this.runnable, callable.runnable)) {
				return false;
			}
			if (!equal(this.returnValue, callable.returnValue)) {
				return false;
			}
			if (!equal(this.exception, callable.exception)) {
				return false;
			}
			return true;
		}
		return false;
	}
}
